/*-------------------------------------------------------------------------
 *
 * queryenvironment.c
 *      Query environment, to store context-specific values like ephemeral named
 *      relations.  Initial use is for named tuplestores for delta information
 *      from "normal" relations.
 *
 * The initial implementation uses a list because the number of such relations
 * in any one context is expected to be very small.  If that becomes a
 * performance problem, the implementation can be changed with no other impact
 * on callers, since this is an opaque structure.  This is the reason to
 * require a create function.
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      src/backend/backend/utils/misc/queryenvironment.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "utils/queryenvironment.h"
#include "utils/rel.h"

/*
 * Private state of a query environment.
 */
struct QueryEnvironment
{
    List       *namedRelList;
};


QueryEnvironment *
create_queryEnv()
{
    return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment));
}

EphemeralNamedRelationMetadata
get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname)
{
    EphemeralNamedRelation enr;

    Assert(refname != NULL);

    if (queryEnv == NULL)
        return NULL;

    enr = get_ENR(queryEnv, refname);

    if (enr)
        return &(enr->md);

    return NULL;
}

/*
 * Register a named relation for use in the given environment.
 *
 * If this is intended exclusively for planning purposes, the tstate field can
 * be left NULL;
 */
void
register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr)
{
    Assert(enr != NULL);
    Assert(get_ENR(queryEnv, enr->md.name) == NULL);

    queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr);
}

/*
 * Unregister an ephemeral relation by name.  This will probably be a rarely
 * used function, but seems like it should be provided "just in case".
 */
void
unregister_ENR(QueryEnvironment *queryEnv, const char *name)
{
    EphemeralNamedRelation match;

    match = get_ENR(queryEnv, name);
    if (match)
        queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match);
}

/*
 * This returns an ENR if there is a name match in the given collection.  It
 * must quietly return NULL if no match is found.
 */
EphemeralNamedRelation
get_ENR(QueryEnvironment *queryEnv, const char *name)
{
    ListCell   *lc;

    Assert(name != NULL);

    if (queryEnv == NULL)
        return NULL;

    foreach(lc, queryEnv->namedRelList)
    {
        EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc);

        if (strcmp(enr->md.name, name) == 0)
            return enr;
    }

    return NULL;
}

/*
 * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was
 * filled.
 *
 * When the TupleDesc is based on a relation from the catalogs, we count on
 * that relation being used at the same time, so that appropriate locks will
 * already be held.  Locking here would be too late anyway.
 */
TupleDesc
ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd)
{
    TupleDesc    tupdesc;

    /* One, and only one, of these fields must be filled. */
    Assert((enrmd->reliddesc == InvalidOid) != (enrmd->tupdesc == NULL));

    if (enrmd->tupdesc != NULL)
        tupdesc = enrmd->tupdesc;
    else
    {
        Relation    relation;

        relation = heap_open(enrmd->reliddesc, NoLock);
        tupdesc = relation->rd_att;
        heap_close(relation, NoLock);
    }

    return tupdesc;
}
